package mbp.core;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
import java.util.TimeZone;

import org.apache.log4j.Logger;

import mbp.base.EnumType;
import mbp.base.FeatureType;
import mbp.base.ResourceType;
import mbp.exceptions.*;

/**
 * Implementuje zasob.
 * @author Wojciech Wilanski
 *
 */
public class Resource
{
	private ResourceType resourceType;
	private HashMap<String, Object> featuresValues;
	private String owner;
	private Date validDate;
	private Integer quantity;
	
	private String uuid;
	
	private Logger logger = Logger.getLogger(this.getClass());
	
	/**
	 * Konstruktor.
	 * @param resourceType
	 * Obiekt ResourceType reprezentujacy typ tworzonego zasobu.
	 */
	public Resource(ResourceType resourceType)
	{
		this.resourceType = resourceType;
		this.owner = "";
		this.validDate = null;
		this.quantity = 1;
		featuresValues = new HashMap<String, Object>();
		//enumTypes = new HashMap<String, String[]>();
		Set<String> keys = this.resourceType.getFeaturesCollection().keySet();
		Iterator<String> it = keys.iterator();
		while(it.hasNext())
		{
			featuresValues.put(it.next(), null);			
		}
		
		
		Date dt = new Date();
		Random random = new Random(); 
		BasicUUID basicUUID = new BasicUUID(0x01010101^dt.getTime(),dt.getTime(),random.nextInt());
		this.uuid = basicUUID.toString();
		
		//LoadEnumArrays();
	}
	
	/**
	 * Konstruktor pozwalajacy na ustawienie identyfikatora zasobu.
	 * @param resourceType
	 * @param uuid
	 */
	public Resource(ResourceType resourceType, String uuid)
	{
		this.resourceType = resourceType;
		this.owner = "";
		this.validDate = null;
		this.quantity = 1;
		featuresValues = new HashMap<String, Object>();
		//enumTypes = new HashMap<String, String[]>();
		Set<String> keys = this.resourceType.getFeaturesCollection().keySet();
		Iterator<String> it = keys.iterator();
		while(it.hasNext())
		{
			featuresValues.put(it.next(), null);			
		}
		
		
		BasicUUID basicUUID = new BasicUUID(uuid);
		this.uuid = basicUUID.toString();
	}
	
	/**
	 * Zwraca obiekt iResourceType reprezentujacy typ zasobu.
	 * @return
	 */
	public ResourceType getType()
	{
		return resourceType;
	}
	
	/**
	 * Zwraca identyfikator zasobu
	 * @return
	 */
	public String getUUID()
	{
		return this.uuid;
	}
	
	/**
	 * Zwraca nazwe wlasciciela zasobu.
	 * @return
	 */
	public String getOwner()
	{
		return owner;
	}
	
	/**
	 * Zwraca date waznosci zasobu.
	 * @return
	 */
	public Date getValidDate()
	{
		return validDate;
	}
	
	/**
	 * Sprawdza wzanosc zasobu.
	 * @return
	 */
	public boolean isValid()
	{
		if(getValidDate()==null) return false;
		Date actualDate = new Date();
		return actualDate.before(getValidDate());
	}
	
	/**
	 * Ustawia nazwe wlasciciela zasobu.
	 * @param owner
	 */
	public void setOwner(String owner)
	{
		this.owner = owner;
	}
	
	/**
	 * Ustawia date waznosci zasobu. UWAGA: Data moze byc ustawiona tylko raz.
	 * @param date
	 */
	public void setValidDate(Date date)
	{
		if(this.validDate == null)
		{
		    this.validDate = date;
		}
	}
	
	/**
	 * Ustawia date waznosci zasobu na podstawie sformatowanego lancucha znakow
	 * [yyyy-MM-dd'T'HH:mm:ss.SSS]
	 * @param dateStr
	 * @throws ParseException
	 */
	public void setValidDateStr(String dateStr) throws ParseException
	{
		if(dateStr.compareTo("0")==0)return;
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
		df.setTimeZone(TimeZone.getTimeZone("UTC"));
		setValidDate( df.parse(dateStr) );
	}
	
	/**
	 * Zwraca date waznosci zasobu jako sformatowany lancuch znakow [yyyy-MM-dd'T'HH:mm:ss.SSS]
	 * @return
	 */
	public String getValidDateStr()
	{
		Date dt = getValidDate();
		if(dt==null)return "0";
		
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
		df.setTimeZone(TimeZone.getTimeZone("UTC"));
		return df.format(dt);
	}
	
	
	/**
	 * Zwraca wartosc danej cechy.
	 * @param featureName
	 * Mazwa cechy
	 * @return Object - Wartosc danej cech rzutowany na typ ogolny.
	 * @throws InvalidFeatureNameException
	 */
	public Object getFeatureValue(String featureName) 
			throws InvalidFeatureNameException
	{
		if(!featuresValues.containsKey(featureName))
		{
			throw new InvalidFeatureNameException("Feature (" + featureName + ") does not exist");
		}
		
		return featuresValues.get(featureName);
	}
	
	/**
	 * Zwraca typ cechy.
	 * @param featureName
	 * Nazwa cechy
	 * @return FeatureType - Typ wyliczeniowy okreslajacy typ danej cechy.
	 * @throws InvalidFeatureNameException
	 */
	public FeatureType getFeatureType(String featureName) 
			throws InvalidFeatureNameException
	{
		if(!resourceType.getFeaturesCollection().containsKey(featureName))
		{
			throw new InvalidFeatureNameException("Feature (" + featureName + ") does not exist");
		}
		return resourceType.getFeaturesCollection().get(featureName);
	}
	
	/**
	 * Ustawia wartosc cechy. Object value musi byc zgodny z typem danej cechy.
	 * @param featureName
	 * Nazwa cechy
	 * @param value
	 * Wartosc cechy
	 * @throws InvalidFeatureNameException
	 * @throws InvalidFeatureTypeException
	 * @throws InvalidFeatureValueException 
	 */
	public void setFeatureValue(String featureName, Object value) 
			throws InvalidFeatureNameException, InvalidFeatureTypeException, InvalidFeatureValueException
	{
		//Sprawdzenie nazwy
		if(!resourceType.getFeaturesCollection().containsKey(featureName))
		{
			throw new InvalidFeatureNameException("Feature (" + featureName + ") does not exist");
		}
		
		//Sprawdzenie typu		
		FeatureType type = (FeatureType)resourceType.getFeaturesCollection().get(featureName);
		//System.out.println("Object Type: " + value.getClass());
		//logger.debug("Got object type: " + value.getClass() );
		switch(type)
		{
			case INT:
				if(!value.getClass().equals(Integer.class))
				{
					throw new InvalidFeatureTypeException("");
				}
				Integer intValue = (Integer)value;
				featuresValues.put(featureName, intValue);
				break;
			case FLOAT:
				Float floatValue = (float) 0.0;
				//Double db = 23.23;
				if(value.getClass().equals(Float.class))
				{
					floatValue = (Float)value;
					//throw new InvalidFeatureTypeException();
				}
				else if(value.getClass().equals(Double.class))
				{
					floatValue = ((Double)value).floatValue();
				}
				else
				{
					throw new InvalidFeatureTypeException();
				}
				featuresValues.put(featureName, floatValue);
				break;
			case ENUM:
				if(featuresValues.get(featureName) == null)
				{
					featuresValues.put( featureName, resourceType.getEnumTypes().get(featureName) );;
				}
				if(value.getClass().equals(Integer.class))
				{
					Integer enumValue = (Integer)value;
					((EnumType)featuresValues.get(featureName)).setValueInt(enumValue);
				}
				else if(value.getClass().equals(String.class))
				{
					String enumValue = (String)value;
					((EnumType)featuresValues.get(featureName)).setValueString(enumValue);
				}
				else
				{
					throw new InvalidFeatureTypeException();
				}
				
				break;
			default:
				throw new InvalidFeatureTypeException();				
		
		}
		
		//System.out.println("SetFeatureValue OK");
		//logger.debug("Setting value: " + value.toString() + " OK");
	}
	
	/**
	 * Zwraca ilosc dobra w sasobie
	 * @return
	 */
	public Integer getQuantity() {
		return quantity;
	}

	/**
	 * Ustawia ilosc dobra w zasobie
	 * @param quantity
	 * @throws InvalidValueException 
	 */
	public void setQuantity(Integer quantity) throws InvalidValueException {
		if(quantity<0)
		{
			throw new InvalidValueException("The value must be positive");
		}
		else
		{
			this.quantity = quantity;
		}
	}

	/**
	 * Metoda testowa - Loguje informacje o zasobie.
	 */
	public void printResourceInfo()
	{
		StringBuilder sb = new StringBuilder();
		
		//System.out.println("--------------------------------------------");
		sb.append("Detailed Resource info:\n");
		
		sb.append(" Resource UUID      : " + getUUID() + "\n");
		
		//System.out.println("Resource Type name : " + GetType().GetResourceTypeName());
		sb.append(" Resource Type name : " + getType().getResourceTypeName() + "\n");
		//System.out.println("Resource Owner     : " + GetOwner());
		sb.append(" Resource Owner     : " + getOwner() + "\n");
		
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
		df.setTimeZone(TimeZone.getTimeZone("UTC"));
		String dts = df.format(getValidDate());
		//System.out.println("Resource Valid Date: " + dts);
		sb.append(" Resource Valid Date: " + dts + "\n");
		//System.out.println("Resource Is Valid  : " + IsValid());
		sb.append(" Resource Is Valid  : " + isValid() + "\n");
		
		sb.append(" Resource quantity  : " + getQuantity() + "\n");
		
		//System.out.println("Features:");
		sb.append(" Features:\n");
		
		Iterator it = getType().getFeaturesCollection().keySet().iterator();
		while(it.hasNext())
		{
			String key = (String)it.next();
			try {
				//System.out.println("Name: " + key  + ",  Type: " + GetFeatureType(key) + ",  Value: " + GetFeatureValue(key));
				sb.append(" Name: " + key  + ",  Type: " + getFeatureType(key) + ",  Value: " + getFeatureValue(key) + "\n");
			} catch (InvalidFeatureNameException e1) {
				// TODO Auto-generated catch block
				logger.error("InvalidFeatureNameException", e1);
			} 
		}
		
		//System.out.println("--------------------------------------------");
		logger.info(sb.toString());
	}
}
